/*
      *问题:富文本编辑器新增支持word文档在线解析功能；
      *修改思路：①修改vue版本为2.6.14；因为wangeditor5.0支持最低vue版本为2.6.14。
                ②修改vue-template-compiler为vue对应版本
                ③安装wangeditor5.0；安装mammoth，在线解析word依赖
                ④修改wangeditor默认粘贴事件，匹配到word文件后进行解析
                ⑤注册自定义‘解析word’菜单
      *相关文件：package.json,package-lock.json;tsconfig.json,yarn.lock.Editor.ts,Editor.vue,wordMenu.ts
   */
// import { Vue } from 'vue-property-decorator';
import Vue from 'vue'
import { checkEnv } from '@/utils/common';
import { getToken } from '@/utils/cookies';
import mammoth from "mammoth";
import { Boot, IDomEditor } from '@wangeditor/editor'
import { Editor, Toolbar } from '@wangeditor/editor-for-vue'
import MyWordMenu from './wordMenu'
// import { uuidGenerator } from '@/utils/common';
// import MyMultiPicMenu from './multiPicPasting'
// import { uuidGenerator } from '@/utils/common';
// import { DomEditor } from '@wangeditor/editor'

const menu1Conf = {//注册的菜单
  key: 'openWord', // 定义 menu key ：要保证唯一、不重复（重要）
  factory() {
    return new MyWordMenu() // 把 `YourMenuClass` 替换为你菜单的 class
  },
}
Boot.registerMenu(menu1Conf)

export default Vue.extend({
  components: { Editor, Toolbar },
  props: {
    disable: {// 启用编辑器
      type: Boolean,
      default: false

    },
  },
  data() {
    return {
      editor: null,
      imgSrc: '',
      html: '',
      editorLoading: false,
      toolbarConfig: {},
      mode: 'default', // or 'simple'
      vHtml: "",
      editorConfig: {
        placeholder: '请输入内容...',
        MENU_CONF: {
          uploadImage: {
            server: '/api/storages/files/upload',
            // form-data fieldName ，默认值 'wangeditor-uploaded-image'
            fieldName: 'file',

            // 单个文件的最大体积限制，默认为 2M
            maxFileSize: 20 * 1024 * 1024, // 20M

            // 最多可上传几个文件，默认为 100
            maxNumberOfFiles: 20,

            // 选择文件时的类型限制，默认为 ['image/*'] 。如不想限制，则设置为 []
            allowedFileTypes: ['image/*'],

            // 自定义上传参数，例如传递验证的 token 等。参数会被添加到 formData 中，一起上传到服务端。
            /* meta: {
              token: '',
            }, */
            // 将 meta 拼接到 url 参数中，默认 false
            metaWithUrl: false,
            // 自定义增加 http  header
            headers: {
              Accept: 'application/json, text/plain, */*',
              Authorization: `Bearer ${getToken()}`,
            },
            // 自定义插入图片
            customInsert(res: any, insertFn: any) {
              // res 即服务端的返回结果
              const { link, originalName, name } = res
              // 从 res 中找到 url alt href ，然后插图图片
              const env = checkEnv();
              const url = res.link;
              insertFn(url, originalName, link)
            },
            // 跨域是否传递 cookie ，默认为 false
            withCredentials: true,
            // 超时时间，默认为 10 秒
            timeout: 5 * 1000, // 5 秒
            // 上传错误，或者触发 timeout 超时
            onError(file: File) {
              if (file.size > 20 * 1024 * 1024) {
                alert(`${file.name} 大小超出限制`)
              } else {
                alert('上传失败')
              }
            },

          },
        },
      }
    }
  },
  mounted() {
  },
  beforeDestroy() {
    const editor: any = this.editor
    if (editor == null) return
    editor.destroy() // 组件销毁时，及时销毁编辑器
  },
  watch: {
    disable(val: any) {
      if (val) {
        const editor: any = this.editor
        editor.disable();
      } else {
        const editor: any = this.editor

        editor.enable();
      }
    }
  },
  methods: {
    //选择要上传的文件
    async fileChange(res: any) {
      if (res.srcElement.files && res.srcElement.files.length > 0) {//如果有要上传的文件
        const file = res.srcElement.files[0]//因为是单选，所以只需要取第一个
        const word = ['docx']//word文件的格式
        const nameArr = file.name.split('.')//取文件名的后缀
        let status = false
        status = word.some((element) => {//如果文件名的后缀在word文件格式内，则进行解析word
          return element === nameArr[nameArr.length - 1]//文件名的后缀
        })
        if (status) {
          const value: any = await this.analysisWord(res.srcElement.files[0])//解析word
          const editor: any = this.editor
          console.log(value)
          editor.dangerouslyInsertHtml(value)//粘贴html格式的内容
        } else {
          this.$message.warning('暂仅支持.docx文件格式')
        }
        res.target.value = ''
      }
    },
    //富文本加载时
    onCreated(editor: any) {
      this.editor = Object.seal(editor) // 一定要用 Object.seal() ，否则会报错
      const toolbarConfig: any = this.toolbarConfig
      toolbarConfig.insertKeys = {//插入自定义的菜单
        index: 20, // 插入的位置，基于当前的 toolbarKeys
        keys: ['openWord']/* 'multiPicPasting' */
      }
      toolbarConfig.excludeKeys = [
        'group-video',
        'insertLink',
        'insertTable'// 排除菜单组，写菜单组 key 的值即可
      ]
      const editor1: any = this.editor
      editor1.on('open-word', this.openWord)//监听开启word解析功能
      /* setTimeout(() => {
        const toolbar: any = DomEditor.getToolbar(editor)

        const curToolbarConfig = toolbar.getConfig()
        console.log(curToolbarConfig.toolbarKeys) // 当前菜单排序和分组
      }, 2000) */



    },
    //开启文本解析功能并打开文件选择器
    openWord() {
      const changeFile: any = document.getElementById("changeFile")
      changeFile.click();
    },
    //解析word
    analysisWord(file: any) {
      return new Promise((resolve) => {
        const reader = new FileReader();
        reader.onload = function (evt: any) {//当文件读取完毕后
          mammoth//调用mammoth组件的方法进行解析文件
            .convertToHtml({ arrayBuffer: evt.target.result })
            .then(function (resultObject) {
              resolve(resultObject.value);//将处理好的html数据返回
            })
            .catch(() => {
              alert('文件解析失败')
            })
            ;
        }
        reader.readAsArrayBuffer(file);// 启动读取指定的 Blob 或 File 内容 ：https://developer.mozilla.org/zh-CN/docs/Web/API/FileReader/readAsArrayBuffer
      })
    },
    /* ------------------------------------以上为在线解析word--------------------------------------------------------- */
    /* ------------------------------------以下为复制粘贴word图片与文字------------------------------------------------ */

    //自定义粘贴。可阻止编辑器的默认粘贴，实现自己的粘贴逻辑。
    async customPaste(editor: IDomEditor, event: any) {
      this.editorLoading = true;
      // event 是 ClipboardEvent 类型，可以拿到粘贴的数据
      // 可参考 https://developer.mozilla.org/zh-CN/docs/Web/API/ClipboardEvent
      let html = event.clipboardData.getData('text/html') // 获取粘贴的 html
      const rtf = event.clipboardData.getData('text/rtf') // 获取 rtf 数据（如从 word wsp 复制粘贴）
      if (html && rtf) {
        // 列表缩进会超出边框，直接过滤掉
        html = html.replace(/text\-indent:\-(.*?)pt/gi, '')
        // 从html内容中查找粘贴内容中是否有图片元素，并返回img标签的属性src值的集合
        const imgSrcs = this.findAllImgSrcsFromHtml(html);
        // 如果有
        if (imgSrcs && Array.isArray(imgSrcs) && imgSrcs.length) {
          // 从rtf内容中查找图片数据
          const rtfImageData = this.extractImageDataFromRtf(rtf);
          // 如果找到
          if (rtfImageData.length) {

            // TODO：此处可以将图片上传到自己的服务器上

            // 执行替换：将html内容中的img标签的src替换成ref中的图片数据，如果上面上传了则为图片路径
            html = await this.replaceImagesFileSourceWithInlineRepresentation(html, imgSrcs, rtfImageData)
            // console.log(html)
            editor.dangerouslyInsertHtml(html);
            // 阻止默认的粘贴行为
            this.editorLoading = false;

            event.preventDefault()
            return false

          }
        }

      }
      this.editorLoading = false;
      return true;
    },


    /**
 * 从html代码中匹配返回图片标签img的属性src的值的集合
 * @param htmlData
 * @return Array
 */
    findAllImgSrcsFromHtml(htmlData: string) {
      const imgReg = /<img(?:(?!\/>).|\n)*?>/gms; //匹配图片中的img标签
      const srcReg =  /file:\/\/\/[^"]+/gm;
      /* let srcReg:any = ''//这个不支持Safari
      if (navigator.userAgent.indexOf("Safari") > -1) {//判断是否Safari浏览器
        srcReg =  /file:\/\/\/[^"]+/gm;
      }else{
        srcReg = /(?<=")file[^"]+(?="[^>]{1,100})/gms//这个不支持Safari
      } */
      // const srcArr = htmlData.match(fileSrc);
      const arr = htmlData.match(imgReg); //筛选出所有的img
      if (!arr || (Array.isArray(arr) && !arr.length)) {
        return false;
      }
      const srcArr = [];
      for (let i = 0; i < arr.length; i++) {
        const src: any = arr[i].match(srcReg);
        // 获取图片地址
        srcArr.push(src[0].replace(/\\/g, '\\'));//拆分后的\会变成\\
      }

      return srcArr;
    },

    /**
       * 从rtf内容中匹配返回图片数据的集合
       * @param rtfData
       * @return Array
       */
    extractImageDataFromRtf(rtfData: string) {
      if (!rtfData) {
        return [];
      }

      const regexPictureHeader = /{\\pict[\s\S]+?({\\\*\\blipuid\s?[\da-fA-F]+)[\s}]*/
      const regexPicture = new RegExp('(?:(' + regexPictureHeader.source + '))([\\da-fA-F\\s]+)\\}', 'g');
      const images = rtfData.match(regexPicture);
      const result = [];

      if (images) {
        for (const image of images) {
          let imageType = '';

          if (image.includes('\\pngblip')) {
            imageType = 'image/png';
          } else if (image.includes('\\jpegblip')) {
            imageType = 'image/jpeg';
          }

          if (imageType) {
            result.push({
              hex: image.replace(regexPictureHeader, '').replace(/[^\da-fA-F]/g, ''),
              type: imageType
            });
          }
        }
      }

      return result;
    },
    /**
     * 将html内容中img标签的属性值替换
     * @param htmlData html内容
     * @param imageSrcs html中img的属性src的值的集合
     * @param imagesHexSources rtf中图片数据的集合，与html内容中的img标签对应
     * @param isBase64Data 是否是Base64的图片数据
     * @return String
     */
    async replaceImagesFileSourceWithInlineRepresentation(htmlData: string, imageSrcs: Array<string>, imagesHexSources: Array<any>, isBase64Data =
      true) {
      if (imageSrcs.length === imagesHexSources.length) {
        htmlData = htmlData.replaceAll('<img', '<div style="display:inline-block;"><img');//将img放在块级元素中，不然不显示

        htmlData = htmlData.replaceAll('<![endif]></span>', '</div><![endif]></span>');
        /* 
        性能优化：
        1、先根据img的文件地址拆分htmlData
        2、只替换拆分后的文件地址
        3、最后将所有数据拼接起来
        */
        const rawData: Array<string> = []//存放拆分数据的数组
        let index = 0//每次拆分时的起始下标
        let lastData = ''//剩余的数据
        for (let i = 0; i < imageSrcs.length; i++) {
          const newSrc = isBase64Data ?
            `data:${imagesHexSources[i].type};base64,${this._convertHexToBase64(imagesHexSources[i].hex)}` :
            imagesHexSources[i];//将base64替换本地文件地址
          const imgUrl = imageSrcs[i];//因为拆分后

          const index1 = htmlData.indexOf(imgUrl);//获取第一次出现的下标
          rawData.push(htmlData.slice(index, index1 + imgUrl.length + 3))//提取出对应的模块

          index = index1 + imgUrl.length + 3//加3个字符，以防万一
          rawData[i] = rawData[i].replace(imgUrl, newSrc)//将截取出来的内容进行替换
          if (i === imageSrcs.length - 1) {//如果是最后一项
            lastData = htmlData.slice(index)//将最后的内容存放起来，用来最后拼接
          }
        }
        htmlData = ''//先置空
        for (let j = 0; j < rawData.length; j++) {
          htmlData += rawData[j]//开始拼接
        }
        htmlData += lastData//拼接最后部分
      }
      return htmlData;
    },

    /**
     * 十六进制转base64
     */
    _convertHexToBase64(hexString: any) {
      return btoa(hexString.match(/\w{2}/g).map((char: any) => {
        return String.fromCharCode(parseInt(char, 16));
      }).join(''));
    },
    

    onClear() {
      const editor: any = this.editor
      editor.clear();
    },

    getHtml() {
      const editor: any = this.editor
      return editor.getHtml();
    },

    setHtml(html: string) {
      const editor: any = this.editor
      editor.dangerouslyInsertHtml(html); // 重新设置编辑器内容
    }
  }




})
